package com.netty.nettyGroupChat;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 *  Handler处理器
 *  ABC 三个客户端。
 *      1，判断是否是当前客户端。
 *      2，判断客户端的状态。  （通过netty提供的周期函数）
 *      3，服务器广播数据给客户端。
 * 业务：
 *      1，用户上线，下线，离开，加入，都广播给其他客户端。
 *      2，用户群聊
 */
public class MyChatServerHandler extends SimpleChannelInboundHandler {
    /**
     *  获取全局的channel
     */
    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    /**
     *  发生读事件的时候，调用该方法。
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        /**
         * 获取当前客户端
         */
        Channel channel = ctx.channel();
        /**
         * 因为channelGroup继承了set集合，所以可以用forEach
         */
        channelGroup.forEach(ch -> {
            System.out.println("客户端："+ch.remoteAddress());
            /**
             * 遍历所有链接的channel，广播给其他客户端
             */
            if (channel != ch){
                System.out.println("有没有执行============");
                /**
                 * TODO
                 * 注意：
                 * 使用writeAndFlush 最后要加"\n"
                 * 不加的话，数据发送不出去。还是待输入的。
                 * 加了才能发出去。
                 */
                ch.writeAndFlush(channel.remoteAddress()+"发送的消息"+msg+"\n");
            }else {
                ch.writeAndFlush("[自己]:"+msg+"\n");
            }
        });
    }

    /**
     *  ctx可以获取channel对象。
     *
     *  因为HandlerAdded方法被我们重写，所以会执行为们重写的方法。
     *  父类super的HandlerAdded是将当前的channel放入到channelGroup当中。
     *  现在因为要广播给其他的客户端，所以是先广播，然后将当前channel加入到channelGroup当中。
     *
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        /**
         * 获取当前channel
         */
        Channel channel = ctx.channel();
        /**
         * 广播给其他客户端。
         * writeAndFlush方法是广播给所有已经在writeAndFlush方法中的channel
         */
        channelGroup.writeAndFlush("服务器端】："+channel.remoteAddress()+"加入\n");
        /**
         * 将当前channel加入到channelGroup。
         * 现在因为要广播给其他的客户端，所以是先广播，然后将当前channel加入到channelGroup当中。
         * 广播之前，当前的channel还没有加入到channelGroup当中。
         */
        channelGroup.add(channel);
    }

    /**
     * 建立链接的时候调用。
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println("服务器端】"+channel.remoteAddress()+"上线\n");
    }

    /**
     *  不活动的时候
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println("服务器端】"+channel.remoteAddress()+"下线\n");
    }

    /**
     * 如果某个channel断开，就调用这个方法。
     *
     *  handlerRemoved 方法，会自动的调用 channelGroup.remove() 方法。
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channelGroup.writeAndFlush("服务器端】"+channel.remoteAddress()+"离开\n");
    }


    /**
     * 抛出异常之后，关闭连接。
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        ctx.close();
    }

}
